
import Cesium from '../Ces/Cesium'
import { fragmentCopy, fragmentCycle, fragmentFlow, fragmentMomentum } from './FluidWaterShaders';


class FluidWater {
  drawCommandMomentum:any// Cesium.ComputeCommand
  drawCommandCopy: any//Cesium.ComputeCommand
  drawCommandCycle: any//Cesium.ComputeCommand
  drawCommandFlow: any//Cesium.ComputeCommand
  drawCommandCopyFlow: any//Cesium.ComputeCommand
  constructor({
    currentWater,
    lastWater,
    flows,
    terrainTexture,
    fluidParameter,
    fluidNeighbor,
    fluidUniform,
    tmp,
  }, context) {
    // 1
    this.setupMomentum(context, fluidParameter, fluidUniform, fluidNeighbor,
      terrainTexture, currentWater, lastWater)

    // 2
    this.drawCommandCopy = this.createCopy(context, currentWater, lastWater)

    // 3
    this.setupCycle(context, fluidParameter, fluidUniform, fluidNeighbor,
      terrainTexture, currentWater)

    // const tmp = fluidParameter.getTempTex(context,
    //   fluidUniform.width,
    //   fluidUniform.height,
    // )

    // 4
    this.setupFlow(context, fluidParameter, fluidUniform, fluidNeighbor,
      currentWater, flows, tmp
    )

    // 5
    this.drawCommandCopyFlow = this.createCopy(context, tmp, flows)
  }

  setupFlow(context, fluidParameter, fluidUniform, fluidNeighbor,
    currentWater, flows, tmp
  ) {
    const fragmentShaderSource = `
      ${fluidParameter.getSource()}
      ${fluidUniform.getSource()}
      ${fluidNeighbor.getSource()}
      ${fragmentFlow}
    `
    const uniformMap = {
      currentWater: () => currentWater,
      flows: () => flows,
    }

    fluidParameter.setupUniformMap(uniformMap)
    fluidUniform.setupUniformMap(uniformMap)
    fluidNeighbor.setupUniformMap(uniformMap)

    this.drawCommandFlow = new Cesium.ComputeCommand({
      outputTexture: tmp,
      shaderProgram: Cesium.ShaderProgram.fromCache({
        context,
        vertexShaderSource: Cesium._shadersViewportQuadVS,
        fragmentShaderSource,
      }),
      vertexArray: context.getViewportQuadVertexArray(),
      uniformMap,
      persists: true,
    })
  }

  createCopy(context, source, outputTexture
  ) {
    return new Cesium.ComputeCommand({
      outputTexture: outputTexture,
      shaderProgram: Cesium.ShaderProgram.fromCache({
        context,
        vertexShaderSource: Cesium._shadersViewportQuadVS,
        fragmentShaderSource: fragmentCopy,
      }),
      vertexArray: context.getViewportQuadVertexArray(),
      uniformMap: {
        source: () => source,
      },
      persists: true,
    })
  }

  setupMomentum(context, fluidParameter, fluidUniform, fluidNeighbor,
    terrainTexture, currentWater, lastWater
  ) {
    const tmp = fluidParameter.getTempTex(context,
      fluidUniform.width,
      fluidUniform.height,
    )

    const uniformMap = {
      terrainTex: () => terrainTexture,
      currentWater: () => currentWater,
      lastWater: () => lastWater,
    }

    fluidUniform.setupUniformMap(uniformMap)
    fluidNeighbor.setupUniformMap(uniformMap)

    const fragmentShaderSource = `
      ${fluidUniform.getSource()}
      ${fluidNeighbor.getSource()}
      ${fragmentMomentum}
    `

    this.drawCommandMomentum = new Cesium.ComputeCommand({
      outputTexture: tmp,
      shaderProgram: Cesium.ShaderProgram.fromCache({
        context,
        vertexShaderSource: Cesium._shadersViewportQuadVS,
        fragmentShaderSource,
      }),
      vertexArray: context.getViewportQuadVertexArray(),
      uniformMap,
      persists: true,
    })
  }

  setupCycle(context, fluidParameter, fluidUniform, fluidNeighbor,
    terrainTexture, currentWater
  ) {
    const tmp = fluidParameter.getTempTex(context,
      fluidUniform.width,
      fluidUniform.height,
    )

    const uniformMap = {
      tmp: () => tmp,
    }

    fluidUniform.setupUniformMap(uniformMap)
    fluidNeighbor.setupUniformMap(uniformMap)
    fluidParameter.setupUniformMap(uniformMap)

    const uniformSource1 = fluidUniform.getSource()
    const uniformSource2 = fluidNeighbor.getSource()
    const uniformSource3 = fluidParameter.getSource()

    const fragmentShaderSource = `
      ${uniformSource1}
      ${uniformSource2}
      ${uniformSource3}
      ${fragmentCycle}
    `
    this.drawCommandCycle = new Cesium.ComputeCommand({
      outputTexture: currentWater,
      shaderProgram: Cesium.ShaderProgram.fromCache({
        context,
        vertexShaderSource: Cesium._shadersViewportQuadVS,
        fragmentShaderSource,
      }),
      vertexArray: context.getViewportQuadVertexArray(),
      uniformMap,
      persists: true,
    })
  }

  update(frameState) {
    frameState.commandList.push(this.drawCommandMomentum); // current last -> tmp
    frameState.commandList.push(this.drawCommandCopy); // current -> last
    frameState.commandList.push(this.drawCommandCycle); // tmp -> current

    frameState.commandList.push(this.drawCommandFlow); // current flow -> tmp
    frameState.commandList.push(this.drawCommandCopyFlow); // tmp -> flow
  }

  destroy() {
    if (this.drawCommandCycle) {
      this.drawCommandCycle.shaderProgram = this.drawCommandCycle.shaderProgram &&
        this.drawCommandCycle.shaderProgram.destroy()
    }
    if (this.drawCommandCopy) {
      this.drawCommandCopy.shaderProgram = this.drawCommandCopy.shaderProgram &&
        this.drawCommandCopy.shaderProgram.destroy()
    }
    if (this.drawCommandMomentum) {
      this.drawCommandMomentum.shaderProgram = this.drawCommandMomentum.shaderProgram &&
        this.drawCommandMomentum.shaderProgram.destroy()
    }
    if (this.drawCommandFlow) {
      this.drawCommandFlow.shaderProgram = this.drawCommandFlow.shaderProgram &&
        this.drawCommandFlow.shaderProgram.destroy()
    }

    if (this.drawCommandCopyFlow) {
      this.drawCommandCopyFlow.shaderProgram = this.drawCommandCopyFlow.shaderProgram &&
        this.drawCommandCopyFlow.shaderProgram.destroy()
    }
    return Cesium.destroyObject(this)
  }

  isDestroyed() {
    return false
  }
}

export default FluidWater
